#!/bin/bash

# Bash Commands
ARCH=$(uname -m)
CLEAR_SCREEN="clear"
PRINT="echo"
READ="read -p"
READ_CHAR="read -n 1"
EXIT="exit"
EXIT_SUCCESS="${EXIT} 0"
EXIT_ERROR="${EXIT} 1"
MAKE_DIR="mkdir -p"
DELETE="rm -r"
COPY="cp -Ra"
MOVE="mv -f"
RENAME="${MOVE}"
READ_PERMISSION="chmod -R a+r"
EXECUTE_PERMISSION="chmod -R a+x"
REMOVE_WRITE_PERMISSION="chmod -R a-w"
CHANGE_DIR="cd"
SHOW_CONTENTS="cat"
RUN_AS_BASH="bash -c"
EXPORT="export"
EXECUTE="exec"
LOGON_USER=$(logname)
CURRENT_USER="sudo -u ${LOGON_USER} ${RUN_AS_BASH}"
ROOT_USER=$(whoami)
CHANGE_OWNER="sudo chown -R ${ROOT_USER}:${ROOT_USER}"

# platform variables
TITLE="InnaITDSC App"

# Installation Source Directories
SOURCE_APP_NAME="InnaITDSCApplication"
SOURCE_APP_DIR="app"
SOURCE_IMG_DIR="images"
SOURCE_PLATFORMS_DIR="platforms"
SOURCE_CONFIG_DIR="config"
SOURCE_SECURITY_DIR="security"

if [[ "$ARCH" == "x86_64" ]]; then
	LIB_ARCH_DIR="x64"
else
	LIB_ARCH_DIR="x86"
fi

SOURCE_LIB_DIR="libraries/${LIB_ARCH_DIR}"

# Installation paths based on 
INSTALL_DIR="/opt/Precision_Biometric"
APPDIR="${INSTALL_DIR}/InnaITDSC"
LIBDIR="${APPDIR}/libraries"
PLATFORMS_DIR="${APPDIR}"
CONFIG_DIR="${INSTALL_DIR}/client-config"
SECURITY_DIR="${INSTALL_DIR}/client-security"
APP_START_SCRIPT="${APPDIR}/InnaITDSC_Run.sh"
AUTO_START_SCRIPT="/etc/xdg/autostart/InnaITDSC_Auto_Run.desktop"
APP_SHORTCUT_DIR="/usr/local/share/applications"
APP_SHORTCUT_SCRIPT="${APP_SHORTCUT_DIR}/InnaIT-DSC.desktop"
UPDATE_ICON_DIR="/usr/share/icons/hicolor"
APP_DRAWER_ICON_DIR="${UPDATE_ICON_DIR}/256x256/apps"
TASKBAR_ICON_DIR="${UPDATE_ICON_DIR}/64x64/apps"
TRAY_ICON_DIR="${UPDATE_ICON_DIR}/48x48/apps"
APP_ICON_NAME="trayIcon"
APP_ICON_IMAGE_NAME="${APP_ICON_NAME}.png"
APP_ICON_IMAGE="${APPDIR}/${SOURCE_IMG_DIR}/${APP_ICON_IMAGE_NAME}"
UDEV_RULES_DIR="/etc/udev/rules.d"
UDEV_RULE_NAME="${UDEV_RULES_DIR}/InnaITDSC_USB.rules"
USB_VENDOR_ID="338c"
USB_PRODUCT_ID="*"


# Function to display usage
usage() {
	${PRINT} "Usage: $0"
	${PRINT} "  Run the script without arguments to see the menu options."
	${EXIT_SUCCESS}
}


# Warning function
warning_function() {
	${PRINT}
	${PRINT}
	${PRINT} "WARNING: $*"

	while true; do
		${PRINT}
		${PRINT} "Press [Enter] key to continue with errors or Press [q/Q] to Exit..."
		${PRINT}
		${READ_CHAR} input

		if [[ "$input" == "" ]]; then
			break;
		elif [[ "$input" == "q" || "$input" == "Q" ]]; then
			${EXIT_ERROR}
			break
		else
			${PRINT} "Invalid input."
			${PRINT}
		fi
	done
}


# Error function
error_function() {
	${PRINT}
	${PRINT}
	${PRINT} "ERROR: $*"
	${PRINT}
	${EXIT_ERROR}
}


# Help function
show_help() {
	${CLEAR_SCREEN}
	${PRINT} "*********************************************************"
	${PRINT} "                M A I N - MENU [DSCGui]                  "
	${PRINT} "*********************************************************"
	${PRINT} "                                                         "
	${PRINT} "| Usage: $0                                             |"
	${PRINT} "|                                                       |"
	${PRINT} "| Installs DscApplication to $APPDIR                    |"
	${PRINT} "| Configures USB device access for the application      |"
	${PRINT} "| Press '1' to install the application.                 |"
	${PRINT} "| Press '2' to uninstall the application.               |"
	${PRINT} "| Press '3' for guidance and step-by-step instructions  |"
	${PRINT} "| Press '4' to exit the installation process.           |"
	${PRINT} "|                                                       |"
	${PRINT} "| Press [Enter] to return to the menu.                  |"
	${PRINT} "| Press [Ctrl+C] to exit the installation process.      |"
	${PRINT} "+-------------------------------------------------------+"
	${PRINT} "| Report bugs to: precisionit.co.in                     |"
	${PRINT} "+-------------------------------------------------------+"
	${PRINT}

	${READ}"Press [Enter] key to continue..." fakeEnterKey
	${PRINT}
}


# Function to configure USB device access
configure_usb_access() {
	# Create the udev rule
	(
${SHOW_CONTENTS} > "${UDEV_RULE_NAME}" << EOF
# Udev rule for InnaITDSC USB device
SUBSYSTEM=="usb", ATTRS{idVendor}=="${USB_VENDOR_ID}", ATTRS{idProduct}=="${USB_PRODUCT_ID}", MODE="0666"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="${USB_VENDOR_ID}", ATTRS{idProduct}=="${USB_PRODUCT_ID}", MODE="0666"
EOF
	) || { error_function "Failed to create ${UDEV_RULE_NAME}"; }


	# Set permissions for the udev rule file
	${READ_PERMISSION} "${UDEV_RULE_NAME}" || { error_function "Failed to set udev rule permissions"; }


	# Reload udev rules and trigger
	if command -v udevadm >/dev/null 2>&1; then
		udevadm control --reload-rules && udevadm trigger || { warning_function "Failed to reload udev rules. USB device access may not work until system restart."; }
	else
		warning_function "udevadm not found. USB device access may not work until system restart.";
	fi


	${PRINT} "USB device access configured successfully."
}

# Installation function
install_app() {
	${PRINT} "Installing ${SOURCE_APP_NAME}..."


	# Create directories
	${MAKE_DIR} "${APPDIR}" || { error_function "Failed to create ${APPDIR}"; }
	${MAKE_DIR} "${LIBDIR}" || { error_function "Failed to create ${LIBDIR}"; }
	${MAKE_DIR} "${PLATFORMS_DIR}" || { error_function "Failed to create ${PLATFORMS_DIR}"; }
	${MAKE_DIR} "${CONFIG_DIR}" || { error_function "Failed to create ${CONFIG_DIR}"; }
	${MAKE_DIR} "${SECURITY_DIR}" || { error_function "Failed to create ${SECURITY_DIR}"; }


	# Copy application files
	${COPY} "$0" ${APPDIR}/ || { error_function "Failed to copy $0"; }
	${COPY} "${SOURCE_APP_DIR}/${SOURCE_APP_NAME}" "${APPDIR}/" || { error_function "Failed to copy ${SOURCE_APP_NAME}"; }
	${EXECUTE_PERMISSION} "${APPDIR}/${SOURCE_APP_NAME}" || { error_function "Failed to set executable permissions for ${SOURCE_APP_NAME}"; }
	${COPY} "${SOURCE_LIB_DIR}"/* "${LIBDIR}/" || { error_function "Failed to copy .so files"; }
	${COPY} "${SOURCE_PLATFORMS_DIR}" "${PLATFORMS_DIR}/" || { error_function "Failed to copy ${SOURCE_PLATFORMS_DIR} files"; }
	${COPY} "${SOURCE_CONFIG_DIR}"/* "${CONFIG_DIR}/" || { error_function "Failed to copy ${SOURCE_CONFIG_DIR} files"; }
	${COPY} "${SOURCE_SECURITY_DIR}"/* "${SECURITY_DIR}/" || { error_function "Failed to copy ${SOURCE_SECURITY_DIR} files"; }
	${COPY} "${SOURCE_IMG_DIR}" "${APPDIR}/" || { error_function "Failed to copy images"; }
	${COPY} "${APP_ICON_IMAGE}" "${APP_DRAWER_ICON_DIR}" || { error_function "Failed to copy icon image"; }
	${COPY} "${APP_ICON_IMAGE}" "${TASKBAR_ICON_DIR}" || { error_function "Failed to copy icon image"; }
	${COPY} "${APP_ICON_IMAGE}" "${TRAY_ICON_DIR}" || { error_function "Failed to copy icon image"; }


	# Configure USB device access
	configure_usb_access || { error_function "Failed to configure USB device access"; }


	# Create wrapper script
	(
${SHOW_CONTENTS} > "${APP_START_SCRIPT}" << EOF
#!/bin/sh
${EXPORT} LD_LIBRARY_PATH="${LIBDIR}/":\$LD_LIBRARY_PATH
${CHANGE_DIR} "${APPDIR}" && ${EXECUTE} ./"${SOURCE_APP_NAME}" "\$@"
EOF
	) || { error_function "Failed to create ${APP_START_SCRIPT}"; }

	${EXECUTE_PERMISSION} "${APP_START_SCRIPT}" || { error_function "Failed to set executable permissions for ${APP_START_SCRIPT} script"; }


	# Create auto start
#	(
#${SHOW_CONTENTS} > "${AUTO_START_SCRIPT}" << EOF
#[Desktop Entry]
#Type=Application
#Exec=${APP_START_SCRIPT}
#Hidden=false
#NoDisplay=false
#X-GNOME-Autostart-enabled=true
#Name=${SOURCE_APP_NAME}
#Comment=Launch ${SOURCE_APP_NAME} at login
#EOF
#	) || { error_function "Failed to create ${AUTO_START_SCRIPT}"; }

#	${EXECUTE_PERMISSION} "${AUTO_START_SCRIPT}" || { error_function "Failed to set executable permissions for ${AUTO_START_SCRIPT} script"; }


	# Create application shortcut
	${MAKE_DIR} "${APP_SHORTCUT_DIR}" || { error_function "Failed to create ${APP_SHORTCUT_DIR}"; }

	(
${SHOW_CONTENTS} > "${APP_SHORTCUT_SCRIPT}" << EOF
[Desktop Entry]
Version=1.0
Type=Application
Name=${SOURCE_APP_NAME}
Comment=Launch ${SOURCE_APP_NAME}
Exec=${APP_START_SCRIPT}
Icon=${APP_ICON_NAME}
Terminal=false
Categories=Utilities;Utility;
StartupWMClass=InnaITDSCApplication
StartupNotify=true
Keywords=Precision;Security;
EOF
	) || { error_function "Failed to create ${APP_SHORTCUT_SCRIPT}"; }
	

	${EXECUTE_PERMISSION} "${APP_SHORTCUT_SCRIPT}" || { error_function "Failed to set permissions for ${APP_SHORTCUT_SCRIPT} file"; }


	# Update desktop database
	if command -v update-desktop-database >/dev/null 2>&1; then
		update-desktop-database "${APP_SHORTCUT_DIR}" 2>/dev/null || { warning_function "Failed to update desktop database. Application may not appear in menu until updated."; }
	else
		warning_function "update-desktop-database not found. Application may not appear in menu until updated.";
	fi


	# Update icon cache
	if command -v gtk-update-icon-cache >/dev/null 2>&1; then
		gtk-update-icon-cache -f "${UPDATE_ICON_DIR}"
	else
		${PRINT} "gtk-update-icon-cache not found. Icons may not appear until updated.";
	fi

	# Update icon cache
	if command -v update-icon-caches >/dev/null 2>&1; then
		update-icon-caches "${UPDATE_ICON_DIR}"
	else
		${PRINT} "update-icon-caches not found. Icons may not appear until updated.";
	fi


	${READ_PERMISSION} "${APPDIR}"

	${REMOVE_WRITE_PERMISSION} "${APPDIR}"

	${CHANGE_OWNER} "${APPDIR}"


	# Run the application in the background
#	if [ -f "${APP_START_SCRIPT}" ]; then
#		if [ -x "${APP_START_SCRIPT}" ]; then
#			${CURRENT_USER} "${APP_START_SCRIPT} > /dev/null 2>&1 &"
#		else
#			error_function "${APP_START_SCRIPT} is not executable. Please check permissions."
#		fi
#	else
#		error_function "${APP_START_SCRIPT} not found. Cannot start the application."
#	fi


	${PRINT} "Installation completed successfully!"
	${PRINT}
	${PRINT}


	${READ}"Press [Enter] key to return to the Main Menu..." fakeEnterKey
}

# Uninstallation function
uninstall_app() {
	${PRINT} "Uninstalling DscApplication..."


	# Kill running instances of the application
	# Look for processes by exact name and related patterns
	pids=$(pgrep -f "${SOURCE_APP_NAME}" || true)
	if [ -n "$pids" ]; then
		for pid in $pids; do
			# Send SIGTERM for graceful termination
			kill -TERM "$pid" 2>/dev/null
			sleep 2  # Wait for graceful termination
			# Check if process is still running
			if kill -0 "$pid" 2>/dev/null; then
				${PRINT} "Process $pid did not terminate gracefully. Forcing termination..."
				kill -KILL "$pid" 2>/dev/null
				sleep 1  # Brief wait to ensure termination
			fi
			# Verify termination
			if ! kill -0 "$pid" 2>/dev/null; then
				${PRINT} "terminated successfully."
			else
				warning_function "Process $pid could not be terminated.";
			fi
		done
	else
		${PRINT} "No running instances of ${SOURCE_APP_NAME} found. Checking for related processes..."
		# Additional check for processes using the application directory
		pids=$(ps aux | grep -i "${APPDIR}" | grep -v grep | awk '{print $2}' || true)
		if [ -n "$pids" ]; then
			for pid in $pids; do
				${PRINT} "Found related process with PID $pid. Attempting to terminate..."
				kill -TERM "$pid" 2>/dev/null
				sleep 2
				if kill -0 "$pid" 2>/dev/null; then
					${PRINT} "Process $pid did not terminate gracefully. Forcing termination..."
					kill -KILL "$pid" 2>/dev/null
					sleep 1
				fi
				if ! kill -0 "$pid" 2>/dev/null; then
					${PRINT} "Process $pid terminated successfully."
				else
					warning_function "Process $pid could not be terminated.";
				fi
			done
		else
			${PRINT} "No related processes found."
		fi
	fi


	# Remove udev rule
	${DELETE} "${UDEV_RULE_NAME}" || { warning_function "Failed to remove ${UDEV_RULE_NAME}"; }


	# Reload udev rules
	if command -v udevadm >/dev/null 2>&1; then
		udevadm control --reload-rules && udevadm trigger || { warning_function "Failed to reload udev rules after uninstallation."; }
	fi


	# Remove installed files and directories
	#${DELETE} "${AUTO_START_SCRIPT}" || { warning_function "Failed to remove ${AUTO_START_SCRIPT}"; }
	${DELETE} "${APP_SHORTCUT_SCRIPT}" || { warning_function "Failed to remove ${APP_SHORTCUT_SCRIPT}"; }
	${DELETE} "${APP_DRAWER_ICON_DIR}/${APP_ICON_IMAGE_NAME}"  || { warning_function "Failed to remove ${APP_ICON_IMAGE_NAME}"; }
	${DELETE} "${TASKBAR_ICON_DIR}/${APP_ICON_IMAGE_NAME}"  || { warning_function "Failed to remove ${APP_ICON_IMAGE_NAME}"; }
	${DELETE} "${TRAY_ICON_DIR}/${APP_ICON_IMAGE_NAME}"  || { warning_function "Failed to remove ${APP_ICON_IMAGE_NAME}"; }
	${DELETE} "${APPDIR}" || { error_function "Failed to remove ${APPDIR}"; }


	# Update desktop database
	if command -v update-desktop-database >/dev/null 2>&1; then
		update-desktop-database "${APP_SHORTCUT_DIR}" 2>/dev/null || { warning_function "Failed to update desktop database after uninstallation."; }
	fi


	# Update icon cache
	if command -v gtk-update-icon-cache >/dev/null 2>&1; then
		gtk-update-icon-cache -f "${UPDATE_ICON_DIR}"
	else
		${PRINT} "gtk-update-icon-cache not found. Icons may not appear until updated.";
	fi
	
	# Update icon cache
	if command -v update-icon-caches >/dev/null 2>&1; then
		update-icon-caches "${UPDATE_ICON_DIR}"
	else
		${PRINT} "update-icon-caches not found. Icons may not appear until updated.";
	fi


	${PRINT} "Uninstallation completed successfully!"
	${PRINT}
	${PRINT}

	${READ}"Press [Enter] key to return to the Main Menu..." fakeEnterKey
}

# Check if script is running with sudo
if [ "$(id -u)" -ne 0 ]; then
	${PRINT} "This script requires sudo privileges to run."
	${PRINT} "Please enter your password when prompted."
	${EXECUTE} sudo "$0" "$@"
	${EXIT} $?
fi

# Handle command line arguments if provided
if [ $# -gt 0 ]; then
	usage
fi

# Display menu and handle choices
while true; do
	${CLEAR_SCREEN}
	${PRINT} "***********************************************"
	${PRINT} "            M A I N - MENU [$TITLE]            "
	${PRINT} "***********************************************"
	${PRINT} ""
	${PRINT} "1. Installation"
	${PRINT} "2. Uninstallation"
	${PRINT} "3. Help"
	${PRINT} "4. Exit"
	${PRINT} ""
	${READ}"Enter your choice [ 1 - 4 ]: " choice

	case $choice in
		1)
			${CLEAR_SCREEN}
			install_app
			;;
		2)
			${CLEAR_SCREEN}
			uninstall_app
			;;
		3)
			show_help
			;;
		4)
			${PRINT} "Exiting DSC Installation Script."
			${EXIT_SUCCESS}
			;;
		*)
			${PRINT} "Invalid option. Please try again."
			${READ}"Press [Enter] key to return to the Main Menu..." fakeEnterKey
			;;
	esac
done

